/*
Copyright (C) 1996-1997 GX Media, Inc.
Copyright (C) 2010 Ronie Salgado

This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

See the GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.

*/

#include "stdafx.h"
#include "QTextureView.h"
#include "QMainFrame.h"
#include "QooleDoc.h"
#include "QooleId.h"
#include "ProgressWindow.h"
#include "Game.h"

// QTextureCell
QTextureCell::QTextureCell(Texture *texture)
	: texture(texture)
{
	columnId = 0;
	rowId = 0;
	x = y = 0;
	width = height = 16;
	cellSize = 16;
	selected = false;
	isFocused = true;

	// Create the bitmap.
	CreateBitmap();
}

QTextureCell::~QTextureCell()
{
}

void QTextureCell::CreateBitmap()
{
	int width = texture->width;
	int height = texture->height;

	// Create the image.
	wxImage image;
	image.Create(width, height, false);

	unsigned char *dst = image.GetData();
	unsigned char *src = (unsigned char *)texture->surface;
	int numpixels = width*height;
	if(texture->bits == 8)
	{
		LPalette pal = texture->game->GetPal();
		for(int i = 0; i < numpixels; i++)
		{
			dst[i*3] = pal.pal[src[i]].red;
			dst[i*3+1] = pal.pal[src[i]].green;
			dst[i*3+2] = pal.pal[src[i]].blue;
		}
	}
	else if(texture->bits == 16)
	{
		unsigned short *src16 = (unsigned short*)src;
		for(int i = 0; i < numpixels; i++)
		{
			unsigned short pix = src16[i];
			dst[i*3] = (pix & 0xF800) >> 8;
			dst[i*3+1] = (pix & 0x07E0) >> 3;
			dst[i*3+2] = (pix & 0x001F) << 3;
		}
	}
	else if(texture->bits == 24)
	{
		memcpy(dst, src, numpixels*3);
	}
	else if(texture->bits == 32)
	{
		// Ignore the alpha channel.
		for(int i = 0; i < numpixels; i++)
		{
			dst[i*3] = src[i*4];
			dst[i*3+1] = src[i*4+1];
			dst[i*3+2] = src[i*4+2];
		}
	}

	bitmap = wxBitmap(image);
	bitdc.SelectObject(bitmap);
}

void QTextureCell::Draw(wxDC &dc)
{
	wxBrush bg;
	if(selected)
	{
		if(isFocused)
			bg.SetColour(64, 64, 192);
		else
			bg.SetColour(128,128,128);
	}
	else
		bg.SetColour(0,0,0);

	// Draw the background.
	dc.SetBrush(bg);
	dc.DrawRectangle(x, y, width, height);

	// Draw the bitmap.
	dc.Blit(x, y, cellSize, cellSize, &bitdc, 0, 0);

	// Draw the text.
	dc.SetTextForeground(*wxWHITE);
	dc.DrawText(texture->GetName(), x, y+cellSize);
}

// QTextureWindow
BEGIN_EVENT_TABLE(QTextureWindow, wxScrolledWindow)
	EVT_LEFT_DOWN(QTextureWindow::OnLButtonDown)
	EVT_LEFT_DCLICK(QTextureWindow::OnLButtonDClick)
	EVT_RIGHT_UP(QTextureWindow::OnRButtonUp)
	EVT_SIZE(QTextureWindow::OnSize)
	EVT_SCROLL_CHANGED(QTextureWindow::OnScroll)
	EVT_MENU(QID_TEXWINDOW_APPLY, QTextureWindow::OnTextureApply)
END_EVENT_TABLE()

QTextureWindow::QTextureWindow(wxWindow *parent)
	: wxScrolledWindow(parent, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxVSCROLL|wxALWAYS_SHOW_SB)
{
	texList = NULL;
	cellWidth = -1;
	cellHeight = -1;
	cellTexSize = 64;
	selectedCell = NULL;
	columns = 0;
	rows = 0;

	// Set the scroll rate.
	SetScrollRate(0, 10);
}

QTextureWindow::~QTextureWindow()
{
	delete texList;
	for(size_t i = 0; i < cells.size(); i++)
		delete cells[i];
}

void QTextureWindow::SetTexList(TexList *list)
{
	if(texList)
		delete texList;
	texList = list;

	if(!texList)
		return;

	int i;
	int numCells = list->GetNumTexs();

	ProgressWindow progWnd(this, _("Loading Textures"), _("Loading"));
	progWnd.Show(true);
	wxEventLoop *loop = wxEventLoop::GetActive();

	// Delete the old cells.
	for(size_t i = 0; i < cells.size(); i++)
		delete cells[i];
	cells.clear();
	cells.reserve(numCells);

	// Create the new cells.
	for(i = 0; i < numCells; i++)
	{
		Texture *texture = texList->GetTexNum(i);
		texture->Cache();

		QTextureCell *cell = new QTextureCell(texture);
		cells.push_back(cell);

		progWnd.SetProgress(i*100/numCells);
		while(i%5 == 0 && loop->Pending())
			loop->Dispatch();

		if(progWnd.Cancelled())
		{
			delete texList;
			texList = NULL;
			return;
		}
	}

	CalcTexCells();
	Refresh();
}

void QTextureWindow::SetCellSize(int size)
{
	cellTexSize = size;

	CalcTexCells();
	Refresh();
}

Texture *QTextureWindow::GetSelTexture(void)
{
	if (selectedCell)
		return selectedCell->texture;
	return NULL;
}

void QTextureWindow::OnSize(wxSizeEvent& event)
{
	CalcTexCells();
	Refresh();
}

void QTextureWindow::OnScroll(wxScrollEvent& event)
{
}

void QTextureWindow::OnLButtonDown(wxMouseEvent &event)
{
	SelectTexture(event.GetPosition());

	SetFocus();
}

void QTextureWindow::OnLButtonDClick(wxMouseEvent &event)
{
	SelectTexture(event.GetPosition());

	wxCommandEvent ev;
	OnTextureApply(ev);
}

void QTextureWindow::OnRButtonUp(wxMouseEvent &event)
{
	SelectTexture(event.GetPosition());

	if(!selectedCell)
		return;

	wxString size;
	size.Printf(wxT("%dx%d"), selectedCell->texture->width,
			selectedCell->texture->height);

	wxMenu menu;
	menu.Append(QID_TEXWINDOW_APPLY, _("Apply"));
	menu.AppendSeparator();
	menu.Append(wxID_ANY, selectedCell->texture->GetName());
	menu.Append(wxID_ANY, size);
	PopupMenu(&menu, event.GetPosition());
}

void QTextureWindow::OnTextureApply(wxCommandEvent &event)
{
	if(!selectedCell)
		return;

	Selector *selector = &GetMainFrame()->GetSelector();

	const wxString &name = selectedCell->texture->GetName();
	int faceIndex = selector->GetSelectFaceIndex();

	if(faceIndex == -1)
	{
		OpTextureApply *op = new OpTextureApply(name);
		GetMainFrame()->CommitOperation(*op);
	}
	else
	{
		OpTextureApplyFace *op = new OpTextureApplyFace(faceIndex, name);
		GetMainFrame()->CommitOperation(*op);
	}
}

void QTextureWindow::OnDraw(wxDC& dc)
{
	// Set the background brush.
	wxBrush bg(wxT("BLACK"), wxSOLID);
	dc.SetBackground(bg);

	// Clear the panel using the background color.
	dc.Clear();

	// Lets try first with a brute force draw all.
	for(size_t i = 0; i < cells.size(); i++)
	{
		QTextureCell *cell = cells[i];
		cell->Draw(dc);
	}
}

void QTextureWindow::CalcTexCells()
{
	// Read the number of cells.
	size_t numCells = cells.size();
	if(numCells == 0)
		return;

	// Get the panel size.
	wxSize size = GetClientSize();

	// Calculate the cell size.
	int cellWidth = cellTexSize;
	int cellHeight = cellTexSize + CellTextHeight;

	// Calculate the number of columns.
	int clientWidth = size.GetWidth() - TableBorder*2; // Remove the left and right border
	columns = Max(clientWidth/cellTexSize, clientWidth/(cellTexSize + TableBorder));

	// Calculate the number of rows.
	if(columns == 0)
		columns = 1;
	rows = numCells/columns;
	if(numCells % columns > 0)
		rows++;

	// Set the virtual size.
	int virtualWidth = TableBorder*(1 + columns) + cellWidth*columns;
	int virtualHeight = TableBorder*(1 + rows) + cellHeight*rows;
	SetVirtualSize(virtualWidth, virtualHeight);

	// Update each cell size.
	for(int r = 0; r < rows; r++)
	{
		for(int c = 0; c < columns; c++)
		{
			size_t id = r*columns + c;
			if(id >= cells.size())
				break;

			QTextureCell *cell = cells[id];

			// Calculate the position.
			int x = TableBorder + (cellWidth + TableBorder)*c;
			int y = TableBorder + (cellHeight + TableBorder)*r;

			// Store the cell data.
			cell->columnId = c;
			cell->rowId = r;
			cell->x = x;
			cell->y = y;
			cell->width = cellWidth;
			cell->height = cellHeight;
			cell->cellSize = cellTexSize;
		}
	}
}

void QTextureWindow::SelectTexture(const wxPoint &point)
{
	wxPoint scrolled = CalcUnscrolledPosition(point);
	int x = scrolled.x;
	int y = scrolled.y;

	// Calculate the cell size.
	int cellWidth = cellTexSize;
	int cellHeight = cellTexSize + CellTextHeight;

	int c = (x - TableBorder) / (cellWidth +  TableBorder);
	int r = (y - TableBorder) / (cellHeight +  TableBorder);

	// Deselect the old selected cell.
	if(selectedCell)
	{
		selectedCell->selected = false;
		selectedCell = NULL;
	}

	int cellId = r*columns + c;
	if(cellId >= cells.size())
	{
		Refresh();
		return;
	}

	QTextureCell *cell = cells[cellId];
	// Check that the points is in the cell
	if(x >= cell->x && x <= cell->x + cell->width
		&& y >= cell->y && y <= cell->y + cell->height)
	{
		selectedCell = cell;
		selectedCell->selected = true;
		Refresh();
		return;
	}
}

// QTextureCurrent
BEGIN_EVENT_TABLE(QTextureCurrent, QTexturePanel)
	EVT_PAINT(QTextureCurrent::OnPaint)
END_EVENT_TABLE()

QTextureCurrent::QTextureCurrent(wxWindow *parent)
	: QTexturePanel(parent, wxID_ANY)
{
}

QTextureCurrent::~QTextureCurrent()
{
}

void QTextureCurrent::OnUpdate(wxView* sender, wxObject* hint)
{
	int flags = 0;

	if(hint && hint->IsKindOf(CLASSINFO(QDocHint)))
	{
		QDocHint *qhint = static_cast<QDocHint*> (hint);
		flags = qhint->flags;
	}

	if(!(flags & (DUAV_OBJSSEL | DUAV_OBJTEXMODATTRIB)))
		return;

	if(!GetMainFrame())
		return;

	Selector *selector = &GetMainFrame()->GetSelector();
	if(!selector)
	{
		SetTexture(NULL);
		return;
	}

	FaceTex *faceTex = selector->GetSelectedFaceTex();
	if(!faceTex)
	{
		SetTexture(NULL);
		return;
	}

	if(faceTex->GetTexture() != texture)
		SetTexture(faceTex->GetTexture());
}

void QTextureCurrent::OnPaint(wxPaintEvent &event)
{
	wxPaintDC dc(this);

	// Set the background brush.
	wxBrush bg(wxT("BLACK"), wxSOLID);
	dc.SetBackground(bg);

	// Clear the panel using the background color.
	dc.Clear();

	if(!texture)
		return;

	int width = texture->width;
	int height = texture->height;

	// Draw the texture.
	dc.Blit(4, 4, width, height, &memDC, 0, 0);

	// Draw some information.
	dc.SetTextForeground(*wxWHITE);
	dc.DrawText(texture->GetShortName(), 72, 8);

	wxString size = wxString::Format(wxT("(%dx%d)"), texture->realWidth, texture->realHeight);
	dc.DrawText(size, 72, 24);
}

// QTextureView
BEGIN_EVENT_TABLE(QTextureView, wxPanel)
	EVT_SHOW(QTextureView::OnShow)
	EVT_CHOICE(QID_TEXLIST_CHOICE, QTextureView::OnListChoice)
	EVT_CHOICE(QID_TEXSIZE_CHOICE, QTextureView::OnSizeChoice)
END_EVENT_TABLE()

QTextureView::QTextureView(wxWindow *parent)
	: wxPanel(parent, QID_TEXTURE_VIEW, wxDefaultPosition, wxSize(160, 600))
{
	buildLater = false;

	// Create the sizer.
	wxBoxSizer *sizer = new wxBoxSizer(wxVERTICAL);

	// Create the current texture viewer.
	currentTextureView = new QTextureCurrent(this);
	sizer->Add(currentTextureView, 1, wxEXPAND);

	// Create a sizer for the choices.
	wxBoxSizer *choiceSizer = new wxBoxSizer(wxHORIZONTAL);
	sizer->Add(choiceSizer, 0, wxEXPAND);

	// Create the texture list choice.
	listChoice = new wxChoice(this, QID_TEXLIST_CHOICE);
	choiceSizer->Add(listChoice, 2, wxEXPAND);

	// Create the texture size choice.
	sizeChoice = new wxChoice(this, QID_TEXSIZE_CHOICE);
	choiceSizer->Add(sizeChoice, 1, wxEXPAND);

	// Create the textures viewer.
	textureWindow = new QTextureWindow(this);
	sizer->Add(textureWindow, 3, wxEXPAND);

	// Store the sizer.
	SetSizer(sizer);
	//sizer->SetSizeHints(this);
}

QTextureView::~QTextureView()
{
}

void QTextureView::OnNewContents(void)
{
	if(IsShown())
		BuildChoices();
	else
		buildLater = true;

	currentTextureView->RegisterDocument(GetMainFrame()->GetDeskTopDocument());
	currentTextureView->SetTexture(NULL);
}

void QTextureView::OnShow(wxShowEvent& event)
{
	if(buildLater)
		BuildChoices();
	buildLater = false;
}

Texture *QTextureView::GetSelTexture(void)
{
	return textureWindow->GetSelTexture();
}

void QTextureView::BuildChoices()
{
	listChoice->Clear();
	texFiles.clear();

	QMainFrame *mainFrame = GetMainFrame();
	if(!mainFrame)
		return;

	QooleDoc *doc = mainFrame->GetDeskTopDocument();
	if(!doc)
		return;

	Game *game = doc->GetGame();
	if(!game)
		return;

	wxString searchDir = wxString::Format(wxT("%s/texlists/%s"), LFile::GetInitDir().c_str(),
			game->GetName().c_str());
	wxString texFile;
	LFindFiles findTexFiles(searchDir, wxT("*.tex"));
	int i = 0;
	for(texFile = findTexFiles.Next(); !texFile.empty(); texFile = findTexFiles.Next())
	{
		wxFileName texName = texFile;
		listChoice->Append(texName.GetName());
		texFiles.push_back(texName.GetFullPath());
	}

	sizeChoice->Clear();
	sizeChoice->Append(wxT("16"));
	sizeChoice->Append(wxT("32"));
	sizeChoice->Append(wxT("64"));
	sizeChoice->Append(wxT("128"));
	sizeChoice->Append(wxT("1:1"));

	switch(textureWindow->cellTexSize)
	{
	default:
	case 16:
		sizeChoice->SetSelection(0);
		break;
	case 32:
		sizeChoice->SetSelection(1);
		break;
	case 64:
		sizeChoice->SetSelection(2);
		break;
	case 128:
		sizeChoice->SetSelection(3);
		break;
	case 256:
		sizeChoice->SetSelection(4);
		break;
	}
	listChoice->SetSelection(0);

	wxCommandEvent ev;
	OnListChoice(ev);
}

void QTextureView::OnListChoice(wxCommandEvent& event)
{
	int num = listChoice->GetSelection();
	if(num != -1 && num < texFiles.size())
		textureWindow->SetTexList(new TexList(texFiles[num],
				GetMainFrame()->GetDeskTopDocument()->GetGame()));
}

void QTextureView::OnSizeChoice(wxCommandEvent& event)
{
	int num = sizeChoice->GetSelection();
	if(num != -1)
		textureWindow->SetCellSize(1 << (num + 4));
}
